﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace GTAHookAdv
{
    /// <summary>
    /// Guess :)
    /// </summary>
    public class TextHelper
    {
        private static Graphics s_graphics = System.Drawing.Graphics.FromImage(global::GTAHookAdv.Properties.Resources.blank);

        private static float s_fontratio = InitFontRatio();
        public static float FontRatio
        {
            get {
                if (float.IsInfinity(s_fontratio))
                    s_fontratio = InitFontRatio();
                if (float.IsInfinity(s_fontratio))
                    return 1f;
                else
                    return s_fontratio;
            }
        }

        /// <summary>
        /// Compare sizes from s_graphics and GTA Font to get a multiplier.
        /// This constant will be used with MesureTextString and depend on ScreenResolution / PixelsPerInches
        /// </summary>
        /// <returns></returns>
        internal static float InitFontRatio()
        {
            using (GTA.Font font = new GTA.Font(100, GTA.FontScaling.Pixel))
            {
                //In perfect world we expect 100 height:
                float graphicHeight = MesureTextSize("A", font,1f).Height; //but s_graphics haves a different pixelPerInch than GTA.Graphics (well this is what i'm suspecting)
                float result =  100f / graphicHeight;
                if (float.IsInfinity(result)) //I redo it i don't call itself to avoid stackoverflow
                {
                    graphicHeight = MesureTextSize("A", font, 1f).Height; //but s_graphics haves a different pixelPerInch than GTA.Graphics (well this is what i'm suspecting)
                    result = 100f / graphicHeight;
                }
                return result;
            }
        }

        /// <summary>
        /// Return needed Size to display the whole text.
        /// </summary>
        /// <param name="text">text to mesure</param>
        /// <param name="font">font used to display</param>
        /// <returns></returns>
        public static Size MesureTextSize(String text, GTA.Font font)
        {

            return MesureTextSize(text, font, FontRatio);
        }

      /// <summary>
        /// For InitFontRatio and MesureTextSize(String text, GTA.Font font) only
      /// </summary>
        private static Size MesureTextSize(String text, GTA.Font font,float forcedRatio)
        {
            
            SizeF floatSize = s_graphics.MeasureString(text, font.WindowsFont, GTAHookAdv.Screen.ResolutionHelper.CurrentResolution.Width-20);
            return new Size(Convert.ToInt32(Math.Ceiling(floatSize.Width * forcedRatio)), Convert.ToInt32(Math.Ceiling(floatSize.Height * forcedRatio)));
        }

        /// <summary>
        /// Return needed Size to display the whole text.
        /// </summary>
        /// <param name="text">text to mesure</param>
        /// <param name="font">font used to display</param>
        /// <returns></returns>
        public static Size MesureTextSize(String text, System.Drawing.Font font)
        {
           
            SizeF floatSize = s_graphics.MeasureString(text, font, GTAHookAdv.Screen.ResolutionHelper.CurrentResolution.Width - 20);
            return new Size(Convert.ToInt32(Math.Ceiling(floatSize.Width * FontRatio)), Convert.ToInt32(Math.Ceiling(floatSize.Height * FontRatio)));
        }
        /// <summary>
        /// Return needed Size to display the whole text.
        /// </summary>
        /// <param name="text">text to mesure</param>
        /// <param name="font">font used to display</param>
        /// <param name="widthMax">the maximum width you are allowing, should not exceed current screen resolution.this argument is usefull if you want to warp text.</param>
        /// <returns></returns>
        public static Size MesureTextSize(String text,GTA.Font font,int widthMax)
        {
            float test = FontRatio;
            SizeF floatSize = s_graphics.MeasureString(text, font.WindowsFont, widthMax);
            return new Size(Convert.ToInt32( Math.Ceiling(floatSize.Width*test)), Convert.ToInt32(Math.Ceiling(floatSize.Height*test)));
        }

        public static string WarpText(string Text, int p,GTA.Font font)
        {
            TextWrapper wrapper = new TextWrapper(Text, p,font.WindowsFont);
            return wrapper.DoJob();
        }

        internal static GTA.Font FontFactory(int pixelHeigh, bool bold, bool italic, GTA.FontEffect effect, Color EdgeColor)
        {
            if (pixelHeigh <= 0)
                throw new ArgumentOutOfRangeException("pixelHeigh must be greater than 0, BTW 10 is allready a very small value !");
            GTA.Font font = new GTA.Font(pixelHeigh, GTA.FontScaling.Pixel, bold, italic);
            font.Effect = effect;
            if (effect != GTA.FontEffect.None)
            {

                font.EffectSize = Convert.ToInt32(Math.Ceiling(((float)pixelHeigh) / 10f));
                if (font.EffectSize == 0)
                    font.EffectSize = 1;
                font.EffectColor = EdgeColor;
            }
            return font;
        }

        internal static GTA.Font CloneFont(GTA.Font _font)
        {
            if (_font == null)
                return null;
            GTA.Font result = new GTA.Font(_font.FontFamily, _font.Height, _font.Scaling, _font.Bold, _font.Italic);
            result.Color = _font.Color;
            result.Effect = _font.Effect;
            result.EffectColor = _font.EffectColor;
            result.EffectSize = _font.EffectSize;
            return result;
        }

        private class TextWrapper
        {
            String[] _lines;
            System.Drawing.Font _font;
            int _widthMax;
            String _remain;
            String _result;
            public TextWrapper(String text, int widthmax, System.Drawing.Font font)
            {
                if (text == null)
                    throw new ArgumentNullException("text");
                if (font == null)
                    throw new ArgumentNullException("font");
                if (widthmax < 1)
                    throw new ArgumentOutOfRangeException("widthmax must be greater than 0");
                _lines = text.Split(new String[] { "\r\n" }, StringSplitOptions.None);
                _widthMax = widthmax;
                _font = font;
            }

            internal string DoJob()
            {
                _remain = String.Empty;
                _result = String.Empty;
                String currentline = String.Empty;
                for (int i = 0; i <= _lines.Length; i++)
                {
                    currentline = _remain;
                    if (i < _lines.Length)
                        currentline += _lines[i];
                    if (MesureTextSize(currentline, _font).Width < _widthMax)
                    {
                        _remain = String.Empty;
                        _result += currentline + "\r\n";
                        continue;
                    }
                    else
                    {
                        CutLine(currentline);
                    }

                }
                return _result;
            }

            Regex rNoSpace = new Regex("[^\\s]+");
            Regex rAlphaNum = new Regex("[a-zA-Z0-9]+");

            private void CutLine(string currentline)
            {
                //FirstTry with spaces
                MatchCollection words = rNoSpace.Matches(currentline);
                if (words.Count > 1)
                {

                    if (CutFromMatches(words, currentline))
                    {
                        CutLineFinalize();
                        return;
                    }

                }
                else
                {
                    //SecondTry spliting by words was not enough, lets split with non alphanumeric chars
                    MatchCollection alphanums = rAlphaNum.Matches(currentline);
                    if (alphanums.Count > 1)
                    {
                        if (CutFromMatches(alphanums, currentline))
                        {
                            CutLineFinalize();
                            return;
                        }
                    }
                    else //Seems the line is composed of alphanums only we'll have to split the word
                    {
                        CutForced(currentline);
                        CutLineFinalize();
                        return;
                    }
                }


            }

            private void CutLineFinalize()
            {
                if (MesureTextSize(_remain, _font).Width >= _widthMax)
                    CutLine(_remain);
                else
                {
                    _result += _remain + "\r\n";
                    _remain = String.Empty;
                }
            }

            /// <summary>
            /// We split the line, and don't care if it'll cut a word
            /// Used as last chance to split the line
            /// </summary>
            /// <param name="currentline"></param>
            private void CutForced(string currentline)
            {
                float multiplier = ((float)MesureTextSize(currentline, _font).Width) / ((float)_widthMax);
                int index = Convert.ToInt32(Math.Floor(((float)currentline.Length) / multiplier));
                try
                {

                    _result += currentline.Substring(0, index) + "\r\n";
                }
                catch
                {
                    throw new Exception(String.Format("index:{0} string : {1}", index, currentline.Length));
                }

                try
                {
                    _remain = currentline.Substring(index, currentline.Length - index);
                }
                catch (Exception)
                {

                    throw new Exception(String.Format("index:{0} string : {1} range : {2}", index, currentline.Length, currentline.Length - index));
                }

            }

            private bool CutFromMatches(MatchCollection words, String currentline)
            {
                for (int i = words.Count - 1; i >= 0; i--)
                {
                    int position = words[i].Index + words[i].Length;
                    String subline = currentline.Substring(0, position);
                    if (MesureTextSize(subline, _font).Width < _widthMax)
                    {
                        _result += subline + "\r\n";
                        _remain = currentline.Substring(position, currentline.Length - position);

                        return true;
                    }
                }
                return false;
            }
        }




       
    }
}
